// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (methods) hexdigest funcs nprimes
#![allow(
    clippy::similar_names,
    clippy::cast_possible_truncation,
    clippy::cast_sign_loss
)]

use crate::common::util::TestScenario;

use std::time::{Duration, SystemTime};

use rand::distributions::{Distribution, Uniform};
use rand::{rngs::SmallRng, Rng, SeedableRng};

const NUM_PRIMES: usize = 10000;
const NUM_TESTS: usize = 100;

#[test]
fn test_invalid_arg() {
    new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}

#[test]
fn test_valid_arg_exponents() {
    new_ucmd!().arg("-h").succeeds().code_is(0);
    new_ucmd!().arg("--exponents").succeeds().code_is(0);
}

#[test]
fn test_repeated_exponents() {
    new_ucmd!()
        .args(&["-hh", "1234", "10240"])
        .succeeds()
        .stdout_only("1234: 2 617\n10240: 2^11 5\n")
        .no_stderr();
}

#[test]
#[cfg(feature = "sort")]
#[cfg(not(target_os = "android"))]
fn test_parallel() {
    use crate::common::util::AtPath;
    use hex_literal::hex;
    use sha1::{Digest, Sha1};
    use std::{fs::OpenOptions, time::Duration};
    use tempfile::TempDir;
    // factor should only flush the buffer at line breaks
    let n_integers = 100_000;
    let mut input_string = String::new();
    for i in 0..=n_integers {
        input_string.push_str(&(format!("{i} "))[..]);
    }

    let tmp_dir = TempDir::new().unwrap();
    let tmp_dir = AtPath::new(tmp_dir.path());
    tmp_dir.touch("output");
    let output = OpenOptions::new()
        .append(true)
        .open(tmp_dir.plus("output"))
        .unwrap();

    for child in (0..10)
        .map(|_| {
            new_ucmd!()
                .timeout(Duration::from_secs(240))
                .set_stdout(output.try_clone().unwrap())
                .pipe_in(input_string.clone())
                .run_no_wait()
        })
        .collect::<Vec<_>>()
    {
        child.wait().unwrap().success();
    }

    let result = TestScenario::new(util_name!())
        .ccmd("sort")
        .arg(tmp_dir.plus("output"))
        .succeeds();
    let mut hasher = Sha1::new();
    hasher.update(result.stdout());
    let hash_check = hasher.finalize();
    assert_eq!(
        hash_check[..],
        hex!("cc743607c0ff300ff575d92f4ff0c87d5660c393")
    );
}

#[test]
fn test_first_1000_integers() {
    use hex_literal::hex;
    use sha1::{Digest, Sha1};

    let n_integers = 1000;
    let mut input_string = String::new();
    for i in 0..=n_integers {
        input_string.push_str(&(format!("{i} "))[..]);
    }

    println!("STDIN='{input_string}'");
    let result = new_ucmd!().pipe_in(input_string.as_bytes()).succeeds();

    // `seq 0 1000 | factor | sha1sum` => "c734327bd18b90fca5762f671672b5eda19f7dca"
    let mut hasher = Sha1::new();
    hasher.update(result.stdout());
    let hash_check = hasher.finalize();
    assert_eq!(
        hash_check[..],
        hex!("c734327bd18b90fca5762f671672b5eda19f7dca")
    );
}

#[test]
fn test_first_1000_integers_with_exponents() {
    use hex_literal::hex;
    use sha1::{Digest, Sha1};

    let n_integers = 1000;
    let mut input_string = String::new();
    for i in 0..=n_integers {
        input_string.push_str(&(format!("{i} "))[..]);
    }

    println!("STDIN='{input_string}'");
    let result = new_ucmd!()
        .arg("-h")
        .pipe_in(input_string.as_bytes())
        .succeeds();

    // Using factor from GNU Coreutils 9.2
    // `seq 0 1000 | factor -h | sha1sum` => "45f5f758a9319870770bd1fec2de23d54331944d"
    let mut hasher = Sha1::new();
    hasher.update(result.stdout());
    let hash_check = hasher.finalize();
    assert_eq!(
        hash_check[..],
        hex!("45f5f758a9319870770bd1fec2de23d54331944d")
    );
}
#[test]
fn test_cli_args() {
    // Make sure that factor works with CLI arguments as well.
    new_ucmd!().args(&["3"]).succeeds().stdout_contains("3: 3");

    new_ucmd!()
        .args(&["3", "6", " +9"])
        .succeeds()
        .stdout_contains("3: 3")
        .stdout_contains("9: 3 3");
}

#[test]
fn test_random() {
    let log_num_primes = f64::from(u32::try_from(NUM_PRIMES).unwrap()).log2().ceil();
    let primes = num_prime::nt_funcs::nprimes(NUM_PRIMES);

    let rng_seed = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs();
    println!("rng_seed={rng_seed:?}");
    let mut rng = SmallRng::seed_from_u64(rng_seed);

    let mut rand_gt = move |min: u64| {
        let mut product = 1_u64;
        let mut factors = Vec::new();
        while product < min {
            // log distribution---higher probability for lower numbers
            let factor = loop {
                let next = rng.gen_range(0_f64..log_num_primes).exp2().floor() as usize;
                if next < NUM_PRIMES {
                    break primes[next];
                }
            };

            match product.checked_mul(factor) {
                Some(p) => {
                    product = p;
                    factors.push(factor);
                }
                None => break,
            };
        }

        factors.sort_unstable();
        (product, factors)
    };

    // build an input and expected output string from factor
    let mut input_string = String::new();
    let mut output_string = String::new();
    for _ in 0..NUM_TESTS {
        let (product, factors) = rand_gt(1 << 63);
        input_string.push_str(&(format!("{product} "))[..]);

        output_string.push_str(&(format!("{product}:"))[..]);
        for factor in factors {
            output_string.push_str(&(format!(" {factor}"))[..]);
        }
        output_string.push('\n');
    }

    run(input_string.as_bytes(), output_string.as_bytes());
}

#[test]
fn test_random_big() {
    let rng_seed = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs();
    println!("rng_seed={rng_seed:?}");
    let mut rng = SmallRng::seed_from_u64(rng_seed);

    let bit_range_1 = Uniform::new(14_usize, 51);
    let mut rand_64 = move || {
        // first, choose a random number of bits for the first factor
        let f_bit_1 = bit_range_1.sample(&mut rng);
        // how many more bits do we need?
        let rem = 64 - f_bit_1;

        // we will have a number of additional factors equal to n_facts + 1
        // where n_facts is in [0, floor(rem/14) )  NOTE half-open interval
        // Each prime factor is at least 14 bits, hence floor(rem/14)
        let n_factors = Uniform::new(0_usize, rem / 14).sample(&mut rng);
        // we have to distribute extra_bits among the (n_facts + 1) values
        let extra_bits = rem - (n_factors + 1) * 14;
        // (remember, a Range is a half-open interval)
        let extra_range = Uniform::new(0_usize, extra_bits + 1);

        // to generate an even split of this range, generate n-1 random elements
        // in the range, add the desired total value to the end, sort this list,
        // and then compute the sequential differences.
        let mut f_bits = Vec::with_capacity(n_factors + 1);
        for _ in 0..n_factors {
            f_bits.push(extra_range.sample(&mut rng));
        }
        f_bits.push(extra_bits);
        f_bits.sort_unstable();

        // compute sequential differences here. We leave off the +14 bits
        // so we can just index PRIMES_BY_BITS
        let mut f_bits = f_bits
            .iter()
            .scan(0, |st, &x| {
                let ret = x - *st; // + 14 would give actual number of bits
                *st = x;
                Some(ret)
            })
            .collect::<Vec<usize>>();
        // finally, add f_bit_1 in there
        f_bits.push(f_bit_1 - 14); // index of f_bit_1 in PRIMES_BY_BITS
        let f_bits = f_bits;

        let mut n_bits = 0;
        let mut product = 1_u64;
        let mut factors = Vec::with_capacity(f_bits.len());
        for bit in f_bits {
            assert!(bit < 37);
            n_bits += 14 + bit;
            let elm = Uniform::new(0, PRIMES_BY_BITS[bit].len()).sample(&mut rng);
            let factor = PRIMES_BY_BITS[bit][elm];
            factors.push(factor);
            product *= factor;
        }
        assert_eq!(n_bits, 64);

        factors.sort_unstable();
        (product, factors)
    };

    let mut input_string = String::new();
    let mut output_string = String::new();
    for _ in 0..NUM_TESTS {
        let (product, factors) = rand_64();
        input_string.push_str(&(format!("{product} "))[..]);

        output_string.push_str(&(format!("{product}:"))[..]);
        for factor in factors {
            output_string.push_str(&(format!(" {factor}"))[..]);
        }
        output_string.push('\n');
    }

    run(input_string.as_bytes(), output_string.as_bytes());
}

#[test]
fn test_big_primes() {
    let mut input_string = String::new();
    let mut output_string = String::new();
    for prime in PRIMES64 {
        input_string.push_str(&(format!("{prime} "))[..]);
        output_string.push_str(&(format!("{prime}: {prime}\n"))[..]);
    }

    run(input_string.as_bytes(), output_string.as_bytes());
}

fn run(input_string: &[u8], output_string: &[u8]) {
    println!("STDIN='{}'", String::from_utf8_lossy(input_string));
    println!(
        "STDOUT(expected)='{}'",
        String::from_utf8_lossy(output_string)
    );
    // now run factor
    new_ucmd!()
        .timeout(Duration::from_secs(240))
        .pipe_in(input_string)
        .run()
        .stdout_is(String::from_utf8(output_string.to_owned()).unwrap());
}

#[test]
fn test_primes_with_exponents() {
    let mut input_string = String::new();
    let mut output_string = String::new();
    for primes in PRIMES_BY_BITS {
        for &prime in *primes {
            input_string.push_str(&(format!("{prime} "))[..]);
            output_string.push_str(&(format!("{prime}: {prime}\n"))[..]);
        }
    }

    println!(
        "STDIN='{}'",
        String::from_utf8_lossy(input_string.as_bytes())
    );
    println!(
        "STDOUT(expected)='{}'",
        String::from_utf8_lossy(output_string.as_bytes())
    );

    // run factor with --exponents
    new_ucmd!()
        .timeout(Duration::from_secs(240))
        .arg("--exponents")
        .pipe_in(input_string)
        .run()
        .stdout_is(String::from_utf8(output_string.as_bytes().to_owned()).unwrap());
}

#[test]
fn fails_on_invalid_number() {
    new_ucmd!().arg("not-a-valid-number").fails();
    new_ucmd!()
        .arg("not-a-valid-number")
        .arg("12")
        .fails()
        .stdout_contains("12: 2 2 3");
}

#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn short_circuit_write_error() {
    use std::fs::OpenOptions;

    // Check that the error is printed exactly once and factor does not move on
    // to the next number when a write error happens.
    //
    // Note: Technically, GNU prints the error twice, not because it does not
    // short circuit the error, but because it always prints the error twice,
    // for any number of inputs. That's silly behavior and printing once is
    // clearly better.
    let f = OpenOptions::new().write(true).open("/dev/full").unwrap();
    new_ucmd!()
        .arg("12")
        .arg("10")
        .set_stdout(f)
        .fails()
        .stderr_is("factor: write error: No space left on device\n");
}

const PRIMES_BY_BITS: &[&[u64]] = &[
    PRIMES14, PRIMES15, PRIMES16, PRIMES17, PRIMES18, PRIMES19, PRIMES20, PRIMES21, PRIMES22,
    PRIMES23, PRIMES24, PRIMES25, PRIMES26, PRIMES27, PRIMES28, PRIMES29, PRIMES30, PRIMES31,
    PRIMES32, PRIMES33, PRIMES34, PRIMES35, PRIMES36, PRIMES37, PRIMES38, PRIMES39, PRIMES40,
    PRIMES41, PRIMES42, PRIMES43, PRIMES44, PRIMES45, PRIMES46, PRIMES47, PRIMES48, PRIMES49,
    PRIMES50,
];

const PRIMES64: &[u64] = &[
    18_446_744_073_709_551_557,
    18_446_744_073_709_551_533,
    18_446_744_073_709_551_521,
    18_446_744_073_709_551_437,
    18_446_744_073_709_551_427,
    18_446_744_073_709_551_359,
    18_446_744_073_709_551_337,
    18_446_744_073_709_551_293,
    18_446_744_073_709_551_263,
    18_446_744_073_709_551_253,
    18_446_744_073_709_551_191,
    18_446_744_073_709_551_163,
    18_446_744_073_709_551_113,
    18_446_744_073_709_550_873,
    18_446_744_073_709_550_791,
    18_446_744_073_709_550_773,
    18_446_744_073_709_550_771,
    18_446_744_073_709_550_719,
    18_446_744_073_709_550_717,
    18_446_744_073_709_550_681,
    18_446_744_073_709_550_671,
    18_446_744_073_709_550_593,
    18_446_744_073_709_550_591,
    18_446_744_073_709_550_539,
    18_446_744_073_709_550_537,
    18_446_744_073_709_550_381,
    18_446_744_073_709_550_341,
    18_446_744_073_709_550_293,
    18_446_744_073_709_550_237,
    18_446_744_073_709_550_147,
    18_446_744_073_709_550_141,
    18_446_744_073_709_550_129,
    18_446_744_073_709_550_111,
    18_446_744_073_709_550_099,
    18_446_744_073_709_550_047,
    18_446_744_073_709_550_033,
    18_446_744_073_709_550_009,
    18_446_744_073_709_549_951,
    18_446_744_073_709_549_861,
    18_446_744_073_709_549_817,
    18_446_744_073_709_549_811,
    18_446_744_073_709_549_777,
    18_446_744_073_709_549_757,
    18_446_744_073_709_549_733,
    18_446_744_073_709_549_667,
    18_446_744_073_709_549_621,
    18_446_744_073_709_549_613,
    18_446_744_073_709_549_583,
    18_446_744_073_709_549_571,
];

const PRIMES14: &[u64] = &[
    16381, 16369, 16363, 16361, 16349, 16339, 16333, 16319, 16301, 16273, 16267, 16253, 16249,
    16231, 16229, 16223, 16217, 16193, 16189, 16187, 16183, 16141, 16139, 16127, 16111, 16103,
    16097, 16091, 16087, 16073, 16069, 16067, 16063, 16061, 16057, 16033, 16007, 16001, 15991,
    15973, 15971, 15959, 15937, 15923, 15919, 15913, 15907, 15901, 15889, 15887, 15881, 15877,
    15859, 15823, 15817, 15809, 15803, 15797, 15791, 15787, 15773, 15767, 15761, 15749, 15739,
    15737, 15733, 15731, 15727, 15683, 15679, 15671, 15667, 15661, 15649, 15647, 15643, 15641,
    15629, 15619, 15607, 15601, 15583, 15581, 15569, 15559, 15551, 15541, 15527, 15511, 15497,
    15493, 15473, 15467, 15461, 15451, 15443, 15439, 15427, 15413, 15401, 15391, 15383, 15377,
    15373,
];

const PRIMES15: &[u64] = &[
    32749, 32719, 32717, 32713, 32707, 32693, 32687, 32653, 32647, 32633, 32621, 32611, 32609,
    32603, 32587, 32579, 32573, 32569, 32563, 32561, 32537, 32533, 32531, 32507, 32503, 32497,
    32491, 32479, 32467, 32443, 32441, 32429, 32423, 32413, 32411, 32401, 32381, 32377, 32371,
    32369, 32363, 32359, 32353, 32341, 32327, 32323, 32321, 32309, 32303, 32299, 32297, 32261,
    32257, 32251, 32237, 32233, 32213, 32203, 32191, 32189, 32183, 32173, 32159, 32143, 32141,
    32119, 32117, 32099, 32089, 32083, 32077, 32069, 32063, 32059, 32057, 32051, 32029, 32027,
    32009, 32003, 31991, 31981, 31973, 31963, 31957, 31907, 31891, 31883, 31873, 31859, 31849,
    31847, 31817, 31799, 31793, 31771, 31769, 31751,
];

const PRIMES16: &[u64] = &[
    65521, 65519, 65497, 65479, 65449, 65447, 65437, 65423, 65419, 65413, 65407, 65393, 65381,
    65371, 65357, 65353, 65327, 65323, 65309, 65293, 65287, 65269, 65267, 65257, 65239, 65213,
    65203, 65183, 65179, 65173, 65171, 65167, 65147, 65141, 65129, 65123, 65119, 65111, 65101,
    65099, 65089, 65071, 65063, 65053, 65033, 65029, 65027, 65011, 65003, 64997, 64969, 64951,
    64937, 64927, 64921, 64919, 64901, 64891, 64879, 64877, 64871, 64853, 64849, 64817, 64811,
    64793, 64783, 64781, 64763, 64747, 64717, 64709, 64693, 64679, 64667, 64663, 64661, 64633,
    64627, 64621, 64613, 64609, 64601, 64591, 64579, 64577, 64567, 64553,
];

const PRIMES17: &[u64] = &[
    131_071, 131_063, 131_059, 131_041, 131_023, 131_011, 131_009, 130_987, 130_981, 130_973,
    130_969, 130_957, 130_927, 130_873, 130_859, 130_843, 130_841, 130_829, 130_817, 130_811,
    130_807, 130_787, 130_783, 130_769, 130_729, 130_699, 130_693, 130_687, 130_681, 130_657,
    130_651, 130_649, 130_643, 130_639, 130_633, 130_631, 130_621, 130_619, 130_589, 130_579,
    130_553, 130_547, 130_531, 130_523, 130_517, 130_513, 130_489, 130_483, 130_477, 130_469,
    130_457, 130_447, 130_439, 130_423, 130_411, 130_409, 130_399, 130_379, 130_369, 130_367,
    130_363, 130_349, 130_343, 130_337, 130_307, 130_303, 130_279, 130_267, 130_261, 130_259,
    130_253, 130_241, 130_223, 130_211, 130_201, 130_199, 130_183, 130_171, 130_147, 130_127,
    130_121, 130_099, 130_087, 130_079, 130_073, 130_069, 130_057, 130_051,
];

const PRIMES18: &[u64] = &[
    262_139, 262_133, 262_127, 262_121, 262_111, 262_109, 262_103, 262_079, 262_069, 262_051,
    262_049, 262_027, 262_007, 261_983, 261_977, 261_973, 261_971, 261_959, 261_917, 261_887,
    261_881, 261_847, 261_823, 261_799, 261_791, 261_787, 261_773, 261_761, 261_757, 261_739,
    261_721, 261_713, 261_707, 261_697, 261_673, 261_643, 261_641, 261_637, 261_631, 261_619,
    261_601, 261_593, 261_587, 261_581, 261_577, 261_563, 261_557, 261_529, 261_523, 261_509,
    261_467, 261_463, 261_451, 261_439, 261_433, 261_431, 261_427, 261_407, 261_389, 261_379,
    261_353, 261_347, 261_337, 261_329, 261_323, 261_301, 261_281, 261_271, 261_251, 261_241,
    261_229, 261_223, 261_169, 261_167, 261_127,
];

const PRIMES19: &[u64] = &[
    524_287, 524_269, 524_261, 524_257, 524_243, 524_231, 524_221, 524_219, 524_203, 524_201,
    524_197, 524_189, 524_171, 524_149, 524_123, 524_119, 524_113, 524_099, 524_087, 524_081,
    524_071, 524_063, 524_057, 524_053, 524_047, 523_997, 523_987, 523_969, 523_949, 523_937,
    523_927, 523_907, 523_903, 523_877, 523_867, 523_847, 523_829, 523_801, 523_793, 523_777,
    523_771, 523_763, 523_759, 523_741, 523_729, 523_717, 523_681, 523_673, 523_669, 523_667,
    523_657, 523_639, 523_637, 523_631, 523_603, 523_597, 523_577, 523_573, 523_571, 523_553,
    523_543, 523_541, 523_519, 523_511, 523_493, 523_489, 523_487, 523_463, 523_459, 523_433,
    523_427, 523_417, 523_403, 523_387, 523_357, 523_351, 523_349, 523_333, 523_307, 523_297,
];

const PRIMES20: &[u64] = &[
    1_048_573, 1_048_571, 1_048_559, 1_048_549, 1_048_517, 1_048_507, 1_048_447, 1_048_433,
    1_048_423, 1_048_391, 1_048_387, 1_048_367, 1_048_361, 1_048_357, 1_048_343, 1_048_309,
    1_048_291, 1_048_273, 1_048_261, 1_048_219, 1_048_217, 1_048_213, 1_048_193, 1_048_189,
    1_048_139, 1_048_129, 1_048_127, 1_048_123, 1_048_063, 1_048_051, 1_048_049, 1_048_043,
    1_048_027, 1_048_013, 1_048_009, 1_048_007, 1_047_997, 1_047_989, 1_047_979, 1_047_971,
    1_047_961, 1_047_941, 1_047_929, 1_047_923, 1_047_887, 1_047_883, 1_047_881, 1_047_859,
    1_047_841, 1_047_833, 1_047_821, 1_047_779, 1_047_773, 1_047_763, 1_047_751, 1_047_737,
    1_047_721, 1_047_713, 1_047_703, 1_047_701, 1_047_691, 1_047_689, 1_047_671, 1_047_667,
    1_047_653, 1_047_649, 1_047_647, 1_047_589, 1_047_587, 1_047_559,
];

const PRIMES21: &[u64] = &[
    2_097_143, 2_097_133, 2_097_131, 2_097_097, 2_097_091, 2_097_083, 2_097_047, 2_097_041,
    2_097_031, 2_097_023, 2_097_013, 2_096_993, 2_096_987, 2_096_971, 2_096_959, 2_096_957,
    2_096_947, 2_096_923, 2_096_911, 2_096_909, 2_096_893, 2_096_881, 2_096_873, 2_096_867,
    2_096_851, 2_096_837, 2_096_807, 2_096_791, 2_096_789, 2_096_777, 2_096_761, 2_096_741,
    2_096_737, 2_096_713, 2_096_693, 2_096_687, 2_096_681, 2_096_639, 2_096_629, 2_096_621,
    2_096_599, 2_096_597, 2_096_569, 2_096_539, 2_096_533, 2_096_483, 2_096_449, 2_096_431,
    2_096_429, 2_096_411, 2_096_407, 2_096_401, 2_096_399, 2_096_377, 2_096_357, 2_096_291,
    2_096_273, 2_096_261, 2_096_233, 2_096_231, 2_096_221, 2_096_209, 2_096_191, 2_096_183,
    2_096_147,
];

const PRIMES22: &[u64] = &[
    4_194_301, 4_194_287, 4_194_277, 4_194_271, 4_194_247, 4_194_217, 4_194_199, 4_194_191,
    4_194_187, 4_194_181, 4_194_173, 4_194_167, 4_194_143, 4_194_137, 4_194_131, 4_194_107,
    4_194_103, 4_194_023, 4_194_011, 4_194_007, 4_193_977, 4_193_971, 4_193_963, 4_193_957,
    4_193_939, 4_193_929, 4_193_909, 4_193_869, 4_193_807, 4_193_803, 4_193_801, 4_193_789,
    4_193_759, 4_193_753, 4_193_743, 4_193_701, 4_193_663, 4_193_633, 4_193_573, 4_193_569,
    4_193_551, 4_193_549, 4_193_531, 4_193_513, 4_193_507, 4_193_459, 4_193_447, 4_193_443,
    4_193_417, 4_193_411, 4_193_393, 4_193_389, 4_193_381, 4_193_377, 4_193_369, 4_193_359,
    4_193_353, 4_193_327, 4_193_309, 4_193_303, 4_193_297,
];

const PRIMES23: &[u64] = &[
    8_388_593, 8_388_587, 8_388_581, 8_388_571, 8_388_547, 8_388_539, 8_388_473, 8_388_461,
    8_388_451, 8_388_449, 8_388_439, 8_388_427, 8_388_421, 8_388_409, 8_388_377, 8_388_371,
    8_388_319, 8_388_301, 8_388_287, 8_388_283, 8_388_277, 8_388_239, 8_388_209, 8_388_187,
    8_388_113, 8_388_109, 8_388_091, 8_388_071, 8_388_059, 8_388_019, 8_388_013, 8_387_999,
    8_387_993, 8_387_959, 8_387_957, 8_387_947, 8_387_933, 8_387_921, 8_387_917, 8_387_891,
    8_387_879, 8_387_867, 8_387_861, 8_387_857, 8_387_839, 8_387_831, 8_387_809, 8_387_807,
    8_387_741, 8_387_737, 8_387_723, 8_387_707, 8_387_671, 8_387_611, 8_387_609, 8_387_591,
];

const PRIMES24: &[u64] = &[
    16_777_213, 16_777_199, 16_777_183, 16_777_153, 16_777_141, 16_777_139, 16_777_127, 16_777_121,
    16_777_099, 16_777_049, 16_777_027, 16_776_989, 16_776_973, 16_776_971, 16_776_967, 16_776_961,
    16_776_941, 16_776_937, 16_776_931, 16_776_919, 16_776_901, 16_776_899, 16_776_869, 16_776_857,
    16_776_839, 16_776_833, 16_776_817, 16_776_763, 16_776_731, 16_776_719, 16_776_713, 16_776_691,
    16_776_689, 16_776_679, 16_776_659, 16_776_631, 16_776_623, 16_776_619, 16_776_607, 16_776_593,
    16_776_581, 16_776_547, 16_776_521, 16_776_491, 16_776_481, 16_776_469, 16_776_451, 16_776_401,
    16_776_391, 16_776_379, 16_776_371, 16_776_367, 16_776_343, 16_776_337, 16_776_317, 16_776_313,
    16_776_289, 16_776_217, 16_776_211,
];

const PRIMES25: &[u64] = &[
    33_554_393, 33_554_383, 33_554_371, 33_554_347, 33_554_341, 33_554_317, 33_554_291, 33_554_273,
    33_554_267, 33_554_249, 33_554_239, 33_554_221, 33_554_201, 33_554_167, 33_554_159, 33_554_137,
    33_554_123, 33_554_093, 33_554_083, 33_554_077, 33_554_051, 33_554_021, 33_554_011, 33_554_009,
    33_553_999, 33_553_991, 33_553_969, 33_553_967, 33_553_909, 33_553_901, 33_553_879, 33_553_837,
    33_553_799, 33_553_787, 33_553_771, 33_553_769, 33_553_759, 33_553_747, 33_553_739, 33_553_727,
    33_553_697, 33_553_693, 33_553_679, 33_553_661, 33_553_657, 33_553_651, 33_553_649, 33_553_633,
    33_553_613, 33_553_607, 33_553_577, 33_553_549, 33_553_547, 33_553_537, 33_553_519, 33_553_517,
    33_553_511, 33_553_489, 33_553_463, 33_553_451, 33_553_417,
];

const PRIMES26: &[u64] = &[
    67_108_859, 67_108_837, 67_108_819, 67_108_777, 67_108_763, 67_108_757, 67_108_753, 67_108_747,
    67_108_739, 67_108_729, 67_108_721, 67_108_709, 67_108_693, 67_108_669, 67_108_667, 67_108_661,
    67_108_649, 67_108_633, 67_108_597, 67_108_579, 67_108_529, 67_108_511, 67_108_507, 67_108_493,
    67_108_471, 67_108_463, 67_108_453, 67_108_439, 67_108_387, 67_108_373, 67_108_369, 67_108_351,
    67_108_331, 67_108_313, 67_108_303, 67_108_289, 67_108_271, 67_108_219, 67_108_207, 67_108_201,
    67_108_199, 67_108_187, 67_108_183, 67_108_177, 67_108_127, 67_108_109, 67_108_081, 67_108_049,
    67_108_039, 67_108_037, 67_108_033, 67_108_009, 67_108_007, 67_108_003, 67_107_983, 67_107_977,
    67_107_967, 67_107_941, 67_107_919, 67_107_913, 67_107_883, 67_107_881, 67_107_871, 67_107_863,
];

const PRIMES27: &[u64] = &[
    134_217_689,
    134_217_649,
    134_217_617,
    134_217_613,
    134_217_593,
    134_217_541,
    134_217_529,
    134_217_509,
    134_217_497,
    134_217_493,
    134_217_487,
    134_217_467,
    134_217_439,
    134_217_437,
    134_217_409,
    134_217_403,
    134_217_401,
    134_217_367,
    134_217_361,
    134_217_353,
    134_217_323,
    134_217_301,
    134_217_277,
    134_217_257,
    134_217_247,
    134_217_221,
    134_217_199,
    134_217_173,
    134_217_163,
    134_217_157,
    134_217_131,
    134_217_103,
    134_217_089,
    134_217_079,
    134_217_049,
    134_217_047,
    134_217_043,
    134_217_001,
    134_216_987,
    134_216_947,
    134_216_939,
    134_216_933,
    134_216_911,
    134_216_899,
    134_216_881,
    134_216_869,
    134_216_867,
    134_216_861,
    134_216_837,
    134_216_827,
    134_216_807,
    134_216_801,
    134_216_791,
    134_216_783,
    134_216_777,
    134_216_759,
    134_216_737,
    134_216_729,
];

const PRIMES28: &[u64] = &[
    268_435_399,
    268_435_367,
    268_435_361,
    268_435_337,
    268_435_331,
    268_435_313,
    268_435_291,
    268_435_273,
    268_435_243,
    268_435_183,
    268_435_171,
    268_435_157,
    268_435_147,
    268_435_133,
    268_435_129,
    268_435_121,
    268_435_109,
    268_435_091,
    268_435_067,
    268_435_043,
    268_435_039,
    268_435_033,
    268_435_019,
    268_435_009,
    268_435_007,
    268_434_997,
    268_434_979,
    268_434_977,
    268_434_961,
    268_434_949,
    268_434_941,
    268_434_937,
    268_434_857,
    268_434_841,
    268_434_827,
    268_434_821,
    268_434_787,
    268_434_781,
    268_434_779,
    268_434_773,
    268_434_731,
    268_434_721,
    268_434_713,
    268_434_707,
    268_434_703,
    268_434_697,
    268_434_659,
    268_434_623,
    268_434_619,
    268_434_581,
    268_434_577,
    268_434_563,
    268_434_557,
    268_434_547,
    268_434_511,
    268_434_499,
    268_434_479,
    268_434_461,
];

const PRIMES29: &[u64] = &[
    536_870_909,
    536_870_879,
    536_870_869,
    536_870_849,
    536_870_839,
    536_870_837,
    536_870_819,
    536_870_813,
    536_870_791,
    536_870_779,
    536_870_767,
    536_870_743,
    536_870_729,
    536_870_723,
    536_870_717,
    536_870_701,
    536_870_683,
    536_870_657,
    536_870_641,
    536_870_627,
    536_870_611,
    536_870_603,
    536_870_599,
    536_870_573,
    536_870_569,
    536_870_563,
    536_870_561,
    536_870_513,
    536_870_501,
    536_870_497,
    536_870_473,
    536_870_401,
    536_870_363,
    536_870_317,
    536_870_303,
    536_870_297,
    536_870_273,
    536_870_267,
    536_870_239,
    536_870_233,
    536_870_219,
    536_870_171,
    536_870_167,
    536_870_153,
    536_870_123,
    536_870_063,
    536_870_057,
    536_870_041,
    536_870_027,
    536_869_999,
    536_869_951,
    536_869_943,
    536_869_937,
    536_869_919,
    536_869_901,
    536_869_891,
];

const PRIMES30: &[u64] = &[
    1_073_741_789,
    1_073_741_783,
    1_073_741_741,
    1_073_741_723,
    1_073_741_719,
    1_073_741_717,
    1_073_741_689,
    1_073_741_671,
    1_073_741_663,
    1_073_741_651,
    1_073_741_621,
    1_073_741_567,
    1_073_741_561,
    1_073_741_527,
    1_073_741_503,
    1_073_741_477,
    1_073_741_467,
    1_073_741_441,
    1_073_741_419,
    1_073_741_399,
    1_073_741_387,
    1_073_741_381,
    1_073_741_371,
    1_073_741_329,
    1_073_741_311,
    1_073_741_309,
    1_073_741_287,
    1_073_741_237,
    1_073_741_213,
    1_073_741_197,
    1_073_741_189,
    1_073_741_173,
    1_073_741_101,
    1_073_741_077,
    1_073_741_047,
    1_073_740_963,
    1_073_740_951,
    1_073_740_933,
    1_073_740_909,
    1_073_740_879,
    1_073_740_853,
    1_073_740_847,
    1_073_740_819,
    1_073_740_807,
];

const PRIMES31: &[u64] = &[
    2_147_483_647,
    2_147_483_629,
    2_147_483_587,
    2_147_483_579,
    2_147_483_563,
    2_147_483_549,
    2_147_483_543,
    2_147_483_497,
    2_147_483_489,
    2_147_483_477,
    2_147_483_423,
    2_147_483_399,
    2_147_483_353,
    2_147_483_323,
    2_147_483_269,
    2_147_483_249,
    2_147_483_237,
    2_147_483_179,
    2_147_483_171,
    2_147_483_137,
    2_147_483_123,
    2_147_483_077,
    2_147_483_069,
    2_147_483_059,
    2_147_483_053,
    2_147_483_033,
    2_147_483_029,
    2_147_482_951,
    2_147_482_949,
    2_147_482_943,
    2_147_482_937,
    2_147_482_921,
    2_147_482_877,
    2_147_482_873,
    2_147_482_867,
    2_147_482_859,
    2_147_482_819,
    2_147_482_817,
    2_147_482_811,
    2_147_482_801,
    2_147_482_763,
    2_147_482_739,
    2_147_482_697,
    2_147_482_693,
    2_147_482_681,
    2_147_482_663,
    2_147_482_661,
];

const PRIMES32: &[u64] = &[
    4_294_967_291,
    4_294_967_279,
    4_294_967_231,
    4_294_967_197,
    4_294_967_189,
    4_294_967_161,
    4_294_967_143,
    4_294_967_111,
    4_294_967_087,
    4_294_967_029,
    4_294_966_997,
    4_294_966_981,
    4_294_966_943,
    4_294_966_927,
    4_294_966_909,
    4_294_966_877,
    4_294_966_829,
    4_294_966_813,
    4_294_966_769,
    4_294_966_667,
    4_294_966_661,
    4_294_966_657,
    4_294_966_651,
    4_294_966_639,
    4_294_966_619,
    4_294_966_591,
    4_294_966_583,
    4_294_966_553,
    4_294_966_477,
    4_294_966_447,
    4_294_966_441,
    4_294_966_427,
    4_294_966_373,
    4_294_966_367,
    4_294_966_337,
    4_294_966_297,
];

const PRIMES33: &[u64] = &[
    8_589_934_583,
    8_589_934_567,
    8_589_934_543,
    8_589_934_513,
    8_589_934_487,
    8_589_934_307,
    8_589_934_291,
    8_589_934_289,
    8_589_934_271,
    8_589_934_237,
    8_589_934_211,
    8_589_934_207,
    8_589_934_201,
    8_589_934_187,
    8_589_934_151,
    8_589_934_141,
    8_589_934_139,
    8_589_934_117,
    8_589_934_103,
    8_589_934_099,
    8_589_934_091,
    8_589_934_069,
    8_589_934_049,
    8_589_934_027,
    8_589_934_007,
    8_589_933_973,
    8_589_933_971,
    8_589_933_967,
    8_589_933_931,
    8_589_933_917,
    8_589_933_907,
    8_589_933_853,
    8_589_933_827,
    8_589_933_823,
    8_589_933_787,
    8_589_933_773,
    8_589_933_733,
    8_589_933_731,
    8_589_933_721,
    8_589_933_683,
    8_589_933_647,
    8_589_933_641,
    8_589_933_637,
    8_589_933_631,
    8_589_933_629,
    8_589_933_619,
    8_589_933_601,
    8_589_933_581,
];

const PRIMES34: &[u64] = &[
    17_179_869_143,
    17_179_869_107,
    17_179_869_071,
    17_179_869_053,
    17_179_869_041,
    17_179_869_019,
    17_179_868_999,
    17_179_868_977,
    17_179_868_957,
    17_179_868_903,
    17_179_868_899,
    17_179_868_887,
    17_179_868_879,
    17_179_868_873,
    17_179_868_869,
    17_179_868_861,
    17_179_868_843,
    17_179_868_833,
    17_179_868_809,
    17_179_868_807,
    17_179_868_777,
    17_179_868_759,
    17_179_868_729,
    17_179_868_711,
    17_179_868_683,
    17_179_868_681,
    17_179_868_597,
    17_179_868_549,
    17_179_868_543,
    17_179_868_521,
    17_179_868_513,
    17_179_868_479,
    17_179_868_443,
    17_179_868_437,
    17_179_868_429,
    17_179_868_383,
    17_179_868_369,
    17_179_868_357,
    17_179_868_353,
    17_179_868_351,
    17_179_868_333,
    17_179_868_317,
    17_179_868_309,
    17_179_868_297,
    17_179_868_287,
    17_179_868_249,
    17_179_868_243,
    17_179_868_183,
];

const PRIMES35: &[u64] = &[
    34_359_738_337,
    34_359_738_319,
    34_359_738_307,
    34_359_738_299,
    34_359_738_289,
    34_359_738_247,
    34_359_738_227,
    34_359_738_121,
    34_359_738_059,
    34_359_738_043,
    34_359_738_011,
    34_359_737_917,
    34_359_737_869,
    34_359_737_849,
    34_359_737_837,
    34_359_737_821,
    34_359_737_813,
    34_359_737_791,
    34_359_737_777,
    34_359_737_771,
    34_359_737_717,
    34_359_737_591,
    34_359_737_567,
    34_359_737_549,
    34_359_737_519,
    34_359_737_497,
    34_359_737_479,
    34_359_737_407,
    34_359_737_393,
    34_359_737_371,
];

const PRIMES36: &[u64] = &[
    68_719_476_731,
    68_719_476_719,
    68_719_476_713,
    68_719_476_671,
    68_719_476_619,
    68_719_476_599,
    68_719_476_577,
    68_719_476_563,
    68_719_476_547,
    68_719_476_503,
    68_719_476_493,
    68_719_476_479,
    68_719_476_433,
    68_719_476_407,
    68_719_476_391,
    68_719_476_389,
    68_719_476_377,
    68_719_476_361,
    68_719_476_323,
    68_719_476_307,
    68_719_476_281,
    68_719_476_271,
    68_719_476_257,
    68_719_476_247,
    68_719_476_209,
    68_719_476_197,
    68_719_476_181,
    68_719_476_169,
    68_719_476_157,
    68_719_476_149,
    68_719_476_109,
    68_719_476_053,
    68_719_476_047,
    68_719_476_019,
    68_719_475_977,
    68_719_475_947,
    68_719_475_933,
    68_719_475_911,
    68_719_475_893,
    68_719_475_879,
    68_719_475_837,
    68_719_475_827,
    68_719_475_809,
    68_719_475_791,
    68_719_475_779,
    68_719_475_771,
    68_719_475_767,
    68_719_475_731,
    68_719_475_729,
];

const PRIMES37: &[u64] = &[
    137_438_953_447,
    137_438_953_441,
    137_438_953_427,
    137_438_953_403,
    137_438_953_349,
    137_438_953_331,
    137_438_953_273,
    137_438_953_271,
    137_438_953_121,
    137_438_953_097,
    137_438_953_037,
    137_438_953_009,
    137_438_952_953,
    137_438_952_901,
    137_438_952_887,
    137_438_952_869,
    137_438_952_853,
    137_438_952_731,
    137_438_952_683,
    137_438_952_611,
    137_438_952_529,
    137_438_952_503,
    137_438_952_491,
];

const PRIMES38: &[u64] = &[
    274_877_906_899,
    274_877_906_857,
    274_877_906_837,
    274_877_906_813,
    274_877_906_791,
    274_877_906_759,
    274_877_906_753,
    274_877_906_717,
    274_877_906_713,
    274_877_906_687,
    274_877_906_647,
    274_877_906_629,
    274_877_906_627,
    274_877_906_573,
    274_877_906_543,
    274_877_906_491,
    274_877_906_477,
    274_877_906_473,
    274_877_906_431,
    274_877_906_419,
    274_877_906_341,
    274_877_906_333,
    274_877_906_327,
    274_877_906_321,
    274_877_906_309,
    274_877_906_267,
    274_877_906_243,
    274_877_906_213,
    274_877_906_209,
    274_877_906_203,
    274_877_906_179,
    274_877_906_167,
    274_877_906_119,
    274_877_906_063,
    274_877_906_053,
    274_877_906_021,
    274_877_905_931,
];

const PRIMES39: &[u64] = &[
    549_755_813_881,
    549_755_813_869,
    549_755_813_821,
    549_755_813_797,
    549_755_813_753,
    549_755_813_723,
    549_755_813_669,
    549_755_813_657,
    549_755_813_647,
    549_755_813_587,
    549_755_813_561,
    549_755_813_513,
    549_755_813_507,
    549_755_813_461,
    549_755_813_417,
    549_755_813_401,
    549_755_813_371,
    549_755_813_359,
    549_755_813_357,
    549_755_813_351,
    549_755_813_339,
    549_755_813_317,
    549_755_813_311,
    549_755_813_281,
    549_755_813_239,
    549_755_813_231,
    549_755_813_213,
    549_755_813_207,
    549_755_813_197,
    549_755_813_183,
    549_755_813_161,
    549_755_813_149,
    549_755_813_147,
    549_755_813_143,
    549_755_813_141,
    549_755_813_059,
    549_755_813_027,
    549_755_813_003,
    549_755_812_951,
    549_755_812_937,
    549_755_812_933,
    549_755_812_889,
    549_755_812_867,
];

const PRIMES40: &[u64] = &[
    1_099_511_627_689,
    1_099_511_627_609,
    1_099_511_627_581,
    1_099_511_627_573,
    1_099_511_627_563,
    1_099_511_627_491,
    1_099_511_627_483,
    1_099_511_627_477,
    1_099_511_627_387,
    1_099_511_627_339,
    1_099_511_627_321,
    1_099_511_627_309,
    1_099_511_627_297,
    1_099_511_627_293,
    1_099_511_627_261,
    1_099_511_627_213,
    1_099_511_627_191,
    1_099_511_627_177,
    1_099_511_627_173,
    1_099_511_627_143,
    1_099_511_627_089,
    1_099_511_626_987,
    1_099_511_626_949,
    1_099_511_626_937,
    1_099_511_626_793,
    1_099_511_626_781,
    1_099_511_626_771,
];

const PRIMES41: &[u64] = &[
    2_199_023_255_531,
    2_199_023_255_521,
    2_199_023_255_497,
    2_199_023_255_489,
    2_199_023_255_479,
    2_199_023_255_477,
    2_199_023_255_461,
    2_199_023_255_441,
    2_199_023_255_419,
    2_199_023_255_413,
    2_199_023_255_357,
    2_199_023_255_327,
    2_199_023_255_291,
    2_199_023_255_279,
    2_199_023_255_267,
    2_199_023_255_243,
    2_199_023_255_203,
    2_199_023_255_171,
    2_199_023_255_137,
    2_199_023_255_101,
    2_199_023_255_087,
    2_199_023_255_081,
    2_199_023_255_069,
    2_199_023_255_027,
    2_199_023_255_021,
    2_199_023_254_979,
    2_199_023_254_933,
    2_199_023_254_913,
    2_199_023_254_907,
    2_199_023_254_903,
    2_199_023_254_843,
    2_199_023_254_787,
    2_199_023_254_699,
    2_199_023_254_693,
    2_199_023_254_657,
    2_199_023_254_567,
];

const PRIMES42: &[u64] = &[
    4_398_046_511_093,
    4_398_046_511_087,
    4_398_046_511_071,
    4_398_046_511_051,
    4_398_046_511_039,
    4_398_046_510_961,
    4_398_046_510_943,
    4_398_046_510_939,
    4_398_046_510_889,
    4_398_046_510_877,
    4_398_046_510_829,
    4_398_046_510_787,
    4_398_046_510_771,
    4_398_046_510_751,
    4_398_046_510_733,
    4_398_046_510_721,
    4_398_046_510_643,
    4_398_046_510_639,
    4_398_046_510_597,
    4_398_046_510_577,
    4_398_046_510_547,
    4_398_046_510_531,
    4_398_046_510_463,
    4_398_046_510_397,
    4_398_046_510_391,
    4_398_046_510_379,
    4_398_046_510_357,
    4_398_046_510_331,
    4_398_046_510_327,
    4_398_046_510_313,
    4_398_046_510_283,
    4_398_046_510_279,
    4_398_046_510_217,
    4_398_046_510_141,
    4_398_046_510_133,
    4_398_046_510_103,
    4_398_046_510_093,
];

const PRIMES43: &[u64] = &[
    8_796_093_022_151,
    8_796_093_022_141,
    8_796_093_022_091,
    8_796_093_022_033,
    8_796_093_021_953,
    8_796_093_021_941,
    8_796_093_021_917,
    8_796_093_021_899,
    8_796_093_021_889,
    8_796_093_021_839,
    8_796_093_021_803,
    8_796_093_021_791,
    8_796_093_021_769,
    8_796_093_021_763,
    8_796_093_021_743,
    8_796_093_021_671,
    8_796_093_021_607,
    8_796_093_021_587,
    8_796_093_021_533,
    8_796_093_021_523,
    8_796_093_021_517,
    8_796_093_021_493,
    8_796_093_021_467,
    8_796_093_021_461,
    8_796_093_021_449,
    8_796_093_021_409,
    8_796_093_021_407,
    8_796_093_021_371,
    8_796_093_021_347,
    8_796_093_021_337,
    8_796_093_021_281,
    8_796_093_021_269,
];

const PRIMES44: &[u64] = &[
    17_592_186_044_399,
    17_592_186_044_299,
    17_592_186_044_297,
    17_592_186_044_287,
    17_592_186_044_273,
    17_592_186_044_267,
    17_592_186_044_129,
    17_592_186_044_089,
    17_592_186_044_057,
    17_592_186_044_039,
    17_592_186_043_987,
    17_592_186_043_921,
    17_592_186_043_889,
    17_592_186_043_877,
    17_592_186_043_841,
    17_592_186_043_829,
    17_592_186_043_819,
    17_592_186_043_813,
    17_592_186_043_807,
    17_592_186_043_741,
    17_592_186_043_693,
    17_592_186_043_667,
    17_592_186_043_631,
    17_592_186_043_591,
    17_592_186_043_577,
    17_592_186_043_547,
    17_592_186_043_483,
    17_592_186_043_451,
    17_592_186_043_433,
    17_592_186_043_409,
];

const PRIMES45: &[u64] = &[
    35_184_372_088_777,
    35_184_372_088_763,
    35_184_372_088_751,
    35_184_372_088_739,
    35_184_372_088_711,
    35_184_372_088_699,
    35_184_372_088_693,
    35_184_372_088_673,
    35_184_372_088_639,
    35_184_372_088_603,
    35_184_372_088_571,
    35_184_372_088_517,
    35_184_372_088_493,
    35_184_372_088_471,
    35_184_372_088_403,
    35_184_372_088_391,
    35_184_372_088_379,
    35_184_372_088_363,
    35_184_372_088_321,
    35_184_372_088_319,
    35_184_372_088_279,
    35_184_372_088_259,
    35_184_372_088_249,
    35_184_372_088_241,
    35_184_372_088_223,
    35_184_372_088_183,
    35_184_372_088_097,
    35_184_372_088_081,
    35_184_372_088_079,
    35_184_372_088_051,
    35_184_372_088_043,
    35_184_372_088_039,
    35_184_372_087_937,
    35_184_372_087_929,
    35_184_372_087_923,
    35_184_372_087_881,
    35_184_372_087_877,
    35_184_372_087_869,
];

const PRIMES46: &[u64] = &[
    70_368_744_177_643,
    70_368_744_177_607,
    70_368_744_177_601,
    70_368_744_177_587,
    70_368_744_177_497,
    70_368_744_177_467,
    70_368_744_177_427,
    70_368_744_177_377,
    70_368_744_177_359,
    70_368_744_177_353,
    70_368_744_177_331,
    70_368_744_177_289,
    70_368_744_177_283,
    70_368_744_177_271,
    70_368_744_177_257,
    70_368_744_177_227,
    70_368_744_177_167,
    70_368_744_177_113,
    70_368_744_177_029,
    70_368_744_176_959,
    70_368_744_176_921,
    70_368_744_176_909,
    70_368_744_176_879,
    70_368_744_176_867,
    70_368_744_176_833,
    70_368_744_176_827,
    70_368_744_176_807,
    70_368_744_176_779,
    70_368_744_176_777,
    70_368_744_176_729,
    70_368_744_176_719,
    70_368_744_176_711,
];

const PRIMES47: &[u64] = &[
    140_737_488_355_213,
    140_737_488_355_201,
    140_737_488_355_181,
    140_737_488_355_049,
    140_737_488_355_031,
    140_737_488_354_989,
    140_737_488_354_893,
    140_737_488_354_787,
    140_737_488_354_709,
    140_737_488_354_679,
    140_737_488_354_613,
    140_737_488_354_557,
    140_737_488_354_511,
    140_737_488_354_431,
    140_737_488_354_413,
    140_737_488_354_409,
    140_737_488_354_373,
    140_737_488_354_347,
    140_737_488_354_329,
];

const PRIMES48: &[u64] = &[
    281_474_976_710_597,
    281_474_976_710_591,
    281_474_976_710_567,
    281_474_976_710_563,
    281_474_976_710_509,
    281_474_976_710_491,
    281_474_976_710_467,
    281_474_976_710_423,
    281_474_976_710_413,
    281_474_976_710_399,
    281_474_976_710_339,
    281_474_976_710_327,
    281_474_976_710_287,
    281_474_976_710_197,
    281_474_976_710_143,
    281_474_976_710_131,
    281_474_976_710_129,
    281_474_976_710_107,
    281_474_976_710_089,
    281_474_976_710_087,
    281_474_976_710_029,
    281_474_976_709_987,
    281_474_976_709_891,
    281_474_976_709_859,
    281_474_976_709_831,
    281_474_976_709_757,
    281_474_976_709_741,
    281_474_976_709_711,
    281_474_976_709_649,
    281_474_976_709_637,
];

const PRIMES49: &[u64] = &[
    562_949_953_421_231,
    562_949_953_421_201,
    562_949_953_421_189,
    562_949_953_421_173,
    562_949_953_421_131,
    562_949_953_421_111,
    562_949_953_421_099,
    562_949_953_421_047,
    562_949_953_421_029,
    562_949_953_420_973,
    562_949_953_420_871,
    562_949_953_420_867,
    562_949_953_420_837,
    562_949_953_420_793,
    562_949_953_420_747,
    562_949_953_420_741,
    562_949_953_420_733,
    562_949_953_420_727,
    562_949_953_420_609,
    562_949_953_420_571,
    562_949_953_420_559,
    562_949_953_420_553,
    562_949_953_420_523,
    562_949_953_420_507,
    562_949_953_420_457,
    562_949_953_420_403,
    562_949_953_420_373,
    562_949_953_420_369,
    562_949_953_420_343,
    562_949_953_420_303,
    562_949_953_420_297,
];

const PRIMES50: &[u64] = &[
    1_125_899_906_842_597,
    1_125_899_906_842_589,
    1_125_899_906_842_573,
    1_125_899_906_842_553,
    1_125_899_906_842_511,
    1_125_899_906_842_507,
    1_125_899_906_842_493,
    1_125_899_906_842_463,
    1_125_899_906_842_429,
    1_125_899_906_842_391,
    1_125_899_906_842_357,
    1_125_899_906_842_283,
    1_125_899_906_842_273,
    1_125_899_906_842_247,
    1_125_899_906_842_201,
    1_125_899_906_842_177,
    1_125_899_906_842_079,
    1_125_899_906_842_033,
    1_125_899_906_842_021,
    1_125_899_906_842_013,
    1_125_899_906_841_973,
    1_125_899_906_841_971,
    1_125_899_906_841_959,
    1_125_899_906_841_949,
    1_125_899_906_841_943,
    1_125_899_906_841_917,
    1_125_899_906_841_901,
    1_125_899_906_841_883,
    1_125_899_906_841_859,
    1_125_899_906_841_811,
    1_125_899_906_841_803,
    1_125_899_906_841_751,
    1_125_899_906_841_713,
    1_125_899_906_841_673,
    1_125_899_906_841_653,
    1_125_899_906_841_623,
    1_125_899_906_841_613,
];

#[test]
fn fails_on_directory() {
    new_ucmd!().pipe_in(".").fails();
}

#[test]
fn succeeds_with_numbers_larger_than_u64() {
    new_ucmd!()
        .arg("158909489063877810457")
        .succeeds()
        .stdout_is("158909489063877810457: 3401347 3861211 12099721\n");
    new_ucmd!()
        .arg("222087527029934481871")
        .succeeds()
        .stdout_is("222087527029934481871: 15601 26449 111427 4830277\n");
    new_ucmd!()
        .arg("12847291069740315094892340035")
        .succeeds()
        .stdout_is(
            "12847291069740315094892340035: \
            5 4073 18899 522591721 63874247821\n",
        );
}

#[test]
fn succeeds_with_numbers_larger_than_u128() {
    new_ucmd!()
        .arg("-h")
        .arg("340282366920938463463374607431768211456")
        .succeeds()
        .stdout_is("340282366920938463463374607431768211456: 2^128\n");
    new_ucmd!()
        .arg("+170141183460469231731687303715884105729")
        .succeeds()
        .stdout_is(
            "170141183460469231731687303715884105729: \
                 3 56713727820156410577229101238628035243\n",
        );
}

#[test]
fn succeeds_with_numbers_larger_than_u256() {
    new_ucmd!()
        .arg("-h")
        .arg(
            "115792089237316195423570985008687907853\
            269984665640564039457584007913129639936",
        )
        .succeeds()
        .stdout_is(
            "115792089237316195423570985008687907853\
                269984665640564039457584007913129639936: 2^256\n",
        );
}
